package date

import (
	"strings"
	"time"

	"gitee.com/hp-huiw/my-go-core/config"
	"github.com/jinzhu/now"
)

// These are part of private members from Go's time package. It can tell you what happens to our date format constants.
//
// const (
//
//	stdLongMonth             = iota + stdNeedDate  // "January"
//	stdMonth                                       // "Jan"
//	stdNumMonth                                    // "1"
//	stdZeroMonth                                   // "01"
//	stdLongWeekDay                                 // "Monday"
//	stdWeekDay                                     // "Mon"
//	stdDay                                         // "2"
//	stdUnderDay                                    // "_2"
//	stdZeroDay                                     // "02"
//	stdUnderYearDay                                // "__2"
//	stdZeroYearDay                                 // "002"
//	stdHour                  = iota + stdNeedClock // "15"
//	stdHour12                                      // "3"
//	stdZeroHour12                                  // "03"
//	stdMinute                                      // "4"
//	stdZeroMinute                                  // "04"
//	stdSecond                                      // "5"
//	stdZeroSecond                                  // "05"
//	stdLongYear              = iota + stdNeedDate  // "2006"
//	stdYear                                        // "06"
//
// )
//
// That's why these date format constants look strange.
const (
	LongDateFormat        = "2006-01-02"          // Treats it as yyyy-MM-dd
	LongDateTimeFormat    = "2006-01-02 15:04:05" // Treats it as yyyy-MM-dd HH:mm:ss
	LongTimestamp         = "200601021504005"
	UTCLongDateTimeFormat = "2006-01-02T15:04:05Z" // Treats it as yyyy-MM-dd HH:mm:ss
)

// Gets current system date.
func GetCurrentDate() time.Time {
	return time.Now()
}

// Gets the first day of the month based on the provided date.
// Example: If date is 2019-12-13, the return value is 2019-12-1.
func GetFirstDayOfMonth(date time.Time) time.Time {
	return now.With(date).BeginningOfMonth()
}

// Gets the first day of the next month based on the provided year and month.
// Example: If date is 2019-12-13, the return value is 2019-12-31.
func GetLastDayOfMonth(date time.Time) time.Time {
	return now.With(date).EndOfMonth()
}

// Parses date string content to time.Time with provided format.
// This uses default location configured in config.dev.yaml.
//
// Golang needs zone offset to parse string to time. Otherwise location is needed.
// We don't want to write the code everywhere.
func ParseDateStringInDefaultLocation(format string, date string) (time.Time, error) {
	if location, err := time.LoadLocation(config.ConfigLoader.GetAppConfig().Timezone.DefaultLocation); err == nil {
		return time.ParseInLocation(format, date, location)
	} else {
		return time.Time{}, err
	}
}

// Parses date string content to time.Time with provided format.
// This uses default location configured in config.dev.yaml.
//
// Golang needs zone offset to parse string to time. Otherwise location is needed.
// We don't want to write the code everywhere.
func ParseDateInDefaultLocation(year int, month int, day int) (time.Time, error) {
	if location, err := time.LoadLocation(config.ConfigLoader.GetAppConfig().Timezone.DefaultLocation); err == nil {
		return time.Date(year, time.Month(month), day, 0, 0, 0, 0, location), nil
	} else {
		return time.Time{}, err
	}
}

// Parses date string content to time.Time with or without UTC format.
// UTC Example: 2024-05-08T16:00:00.000Z
func ParseFromLongDateTime(dateStr string) (time.Time, error) {
	if strings.Contains(dateStr, "T") || strings.HasSuffix(dateStr, "Z") {
		return time.Parse(time.RFC3339, dateStr)
	}

	return ParseDateStringInDefaultLocation(LongDateTimeFormat, dateStr)
}

// Converts date to string format.
// We use UTC as default format.
func ConvertToDateString(date time.Time) string {
	return date.UTC().Format(UTCLongDateTimeFormat)
}
